跳到主要内容

使用 Makefile 构建项目

makefile 是什么?

就是一个编译编排工具,用于快速构建程序,例如 Maven 那样

比如:

$ go build -o hello hello.go
$ ./hello

使用 Makefile,我们可以很轻松自定义一个 target 完成这个任务

.PHONY: buildandrun
BIN_FILE=hello

buildandrun:
@go build -o "${BIN_FILE}" hello.go
./"${BIN_FILE}"

然后我们就可以用如下命令完成工作了

make
./"hello"
hello world

如何在 Windows 上使用 Makefile,参考这篇 博客

书写规则

首先来理解一下 Makefile 的书写规则,规则包含两个部分,一个是 依赖关系,一个是生成目标的方法。

example:

foo.o: foo.c defs.h       # foo模块
cc -c -g foo.c

这里第一行声明了 foo.o 是我们的目标, foo.c 和 defs.h 是目标所依赖的源文件,第二行表示了如何生成这个文件的命令

文件的依赖关系, foo.o 依赖于 foo.c 和 defs.h 的文件,如果 foo.c 和 defs.h 的文件日期要比 foo.o 文件日期要新,或是 foo.o 不存在,那么依赖关系发生。

简单的例子

.PHONY: build clean tool lint help

all: build

build:
go build -v .

tool:
go vet . |& grep -v vendor; true
gofmt -w .

lint:
golint ./...

clean:
rm -rf go-gin-example
go clean -i .

help:
@echo "make: compile packages and dependencies"
@echo "make tool: run specified go tool"
@echo "make lint: golint ./..."
@echo "make clean: remove object files and cached files"

在命令行执行即可看见效果,实现了以下功能:

  1. make: make 就是 make all
  2. make build: 编译当前项目的包和依赖项
  3. make tool: 运行指定的 Go 工具集
  4. make lint: golint 一下
  5. make clean: 删除对象文件和缓存文件
  6. make help: help

.PHONY 伪目标

在上述文件中,使用了 .PHONY,其作用是声明 build / clean / tool / lint / help 为伪目标,声明为伪目标会怎么样呢?

使用伪目标有两点原因:

  • 避免我们的 Makefile 中定义的只执行的命令的目标和工作目录下的实际文件出现名字冲突。
  • 提高执行 make 时的效率,特别是对于一个大型的工程来说,提高编译的效率也是我们所必需的。
clean:
rm -rf *.o test

因为,我们并不生成 “clean” 这个文件。“伪目标” 并不是一个文件,只是一个标签,由于 “伪目标” 不是文件,所以 make 无法生成它的依赖关系和决定它是否要执行。

我们只有通过显式地指明这个“目标”才能让其生效。当然,“伪目标” 的取名不能和文件名重名,不然其就失去了 “伪目标” 的意义了。

.PHONY:clean
clean:
rm -rf *.o test

这样 clean 就被声明成一个伪目标,无论当前目录下是否存在 clean 这个文件,当我们执行 make clean 后 rm 都会被执行。

显示命令输出

通常,make 会把其要执行的命令行在命令执行前输出到屏幕上。当我们用 @ 字符在命令行前,那么,这个命令将不被 make 显示出来,最具代表性的例子是,我们用这个功能来向屏幕显示一些信息。如:

view:
echo "Hello world!"

执行输出:

$ make view
echo "Hello world!"
Hello world!

更改成 @

view:
@echo "Hello world!"

执行输出:

$ make view
Hello world!

可以发现这个命令就不会被回显了

区分不同的操作系统

因为可能需要根据不同的平台执行不同的命令,例如 Windows 和 Linux 很多命令是不一样的

注意!! 这些 ifeq 前面不能有空格

ifeq ($(OS),Windows_NT)
PLATFORM="Windows"
else
ifeq ($(shell uname),Darwin)
PLATFORM="MacOS"
else
PLATFORM="Unix-Like"
endif
endif

all:
@echo $(PLATFORM)

如果还需要根据不同的架构进行设置可以这样:

ifeq ($(OS),Windows_NT)
CCFLAGS += -D WIN32
ifeq ($(PROCESSOR_ARCHITEW6432),AMD64)
CCFLAGS += -D AMD64
else
ifeq ($(PROCESSOR_ARCHITECTURE),AMD64)
CCFLAGS += -D AMD64
endif
ifeq ($(PROCESSOR_ARCHITECTURE),x86)
CCFLAGS += -D IA32
endif
endif
else
UNAME_S := $(shell uname -s)
ifeq ($(UNAME_S),Linux)
CCFLAGS += -D LINUX
endif
ifeq ($(UNAME_S),Darwin)
CCFLAGS += -D OSX
endif
UNAME_P := $(shell uname -p)
ifeq ($(UNAME_P),x86_64)
CCFLAGS += -D AMD64
endif
ifneq ($(filter %86,$(UNAME_P)),)
CCFLAGS += -D IA32
endif
ifneq ($(filter arm%,$(UNAME_P)),)
CCFLAGS += -D ARM
endif
endif

这里根据不同的平台替换不同的命令:

ifeq ($(LANG),)

# Customize for Windows
# The MIPS gcc compiler must use the cygwin1.dll that came with the compiler.
CC_X86 = cl /O1 /nologo
CP = copy
RM = del
DWIN32 = -DWIN32
BIN_MIPS = ..\gccmips_elf
VHDL_DIR = ..\vhdl
LINUX_PWD =
GCC_MIPS = $(BIN_MIPS)\gcc $(CFLAGS)
AS_MIPS = $(BIN_MIPS)\as
LD_MIPS = $(BIN_MIPS)\ld
DUMP_MIPS = $(BIN_MIPS)\objdump
CONVERT_BIN = $(LINUX_PWD)convert_bin.exe

else

# Customized for Linux
# See http://www.opencores.com/projects.cgi/web/mips/linux_tools.htm
CC_X86 = gcc -Wall -O -g
CP = cp
RM = rm -rf
DWIN32 =
BIN_MIPS =
VHDL_DIR = ../vhdl
LINUX_PWD = ./
#GCC_MIPS = $(BIN_MIPS)mips-elf-gcc $(CFLAGS)
#AS_MIPS = $(BIN_MIPS)mips-elf-as
#LD_MIPS = $(BIN_MIPS)mips-elf-ld
#DUMP_MIPS = $(BIN_MIPS)mips-elf-objdump
GCC_MIPS = mips-uclibc-gcc $(CFLAGS)
AS_MIPS = mips-uclibc-as
LD_MIPS = mips-uclibc-ld
DUMP_MIPS = mips-uclibc-objdump
CONVERT_BIN = $(LINUX_PWD)convert_bin.exe
#CONVERT_BIN = $(BIN_MIPS)mips-elf-objcopy -I elf32-big -O binary test.axf test.bin

endif

一般情况下,linux是有 LANG这个环境变量 而window没有的,所有通过 ifeq ($(LANG),) 是否为空即可判断操作系统。但是,不一般的情况,比如装个 matlab,系统会增加环境变量 Lang ,于是, gmake all 出错了。如果出错直接改成 ifeq ($(OS),Windows_NT)

创建变量

1、局部变量

作用域只在指定目标及连带规则中

target : name <assignment> value
target : override name <assignment> value

示例:

var := var_start
test : var := var_test

test :
@echo "test:"
@echo "var => $(var)"

another :
@echo "another:"
@echo "var => $(var)"

使用 Bash

test-local: SHELL:=/bin/bash
test-local: copy-same-pkg-proto
@if [ ! -e "main_test.go" ]; then echo -e "package main \n import \"testing\" \n func Test_StartMain(t *testing.T) { main() }">main_test.go; VAR=1; echo "ccc ${VAR}"; fi
go test -coverpkg="./..." -c -o cover.test
@if [ ${VAR} -eq 1 ]; then rm main_test.go; fi

Windows 环境安装 make

下载文件

https://sourceforge.net/projects/mingw/files/latest/download?source=files
mingw-get-setup.exe

添加 mingw\bin 到系统的环境变量 path 中,并将 mingw\bin\mingw32-make.exe 复制一份为 mingw\bin\make.exe

Reference